package com.dirox.confluence.labelmanagement.action;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.lang.StringUtils;

import com.atlassian.confluence.event.Evented;
import com.atlassian.confluence.event.events.label.LabelListViewEvent;
import com.atlassian.confluence.labels.Label;
import com.atlassian.confluence.labels.actions.AbstractLabelDisplayingAction;
import com.atlassian.confluence.spaces.SpaceManager;
import com.atlassian.confluence.user.SessionKeys;
import com.atlassian.confluence.util.actions.AlphabeticalLabelGroupingSupport;
import com.dirox.confluence.labelmanagement.service.LabelManagementService;
import com.dirox.confluence.labelmanagement.utils.SpaceNotFoundException;
import com.opensymphony.xwork.ActionContext;

/**
 * This class displays labels management
 */
public class ViewLabelManagement extends AbstractLabelDisplayingAction
		implements Evented<LabelListViewEvent> {

	private static final long serialVersionUID = 1L;
	private AlphabeticalLabelGroupingSupport alphabeticalLabelGroupingSupport = null;

	private LabelManagementService labelManagementService;
	private SpaceManager spaceManager;

	/**
	 * Get map with key is labelName and value is the number of contents
	 * associated to label
	 * 
	 * @param spaceKey
	 * @return Map<String, Integer>
	 * @throws SpaceNotFoundException
	 */
	public Map<Label, Integer> countNumberOfAffectedContent(String spaceKey)
			throws SpaceNotFoundException {
		Map<Label, Integer> results = new HashMap<Label, Integer>();
		if (StringUtils.isNotBlank(spaceKey)) {
			if (spaceManager.getSpace(spaceKey) == null) {
				throw new SpaceNotFoundException(
						getText("label-management.error.not.exist.space"));
			}
			results = labelManagementService.getLabelsCountInSpace(spaceKey);
		} else {
			results = labelManagementService.getLabelsCountInAllSpaces();
		}
		return sortByComparator(results);
	}

	@Override
	public LabelListViewEvent getEventToPublish(String result) {
		return new LabelListViewEvent(this, getSpace(), "LabelManagement");
	}

	public AlphabeticalLabelGroupingSupport getAlphabeticalLabelGroupingSupport() {
		if (alphabeticalLabelGroupingSupport == null) {
			Set<Label> labels = null;
			if (getSpace() != null) {
				labels = labelManagementService.getLabelsCountInSpace(
						getSpace().getKey()).keySet();
			} else {
				labels = labelManagementService.getLabelsCountInAllSpaces()
						.keySet();
			}
			alphabeticalLabelGroupingSupport = new AlphabeticalLabelGroupingSupport(
					labels);
		}
		return alphabeticalLabelGroupingSupport;
	}

	public boolean isGroupRanks() {
		Boolean groupRanks = (Boolean) ActionContext.getContext().getSession()
				.get(SessionKeys.HEATMAP_GROUP_RANKS);
		return groupRanks != null && groupRanks.booleanValue();
	}

	@SuppressWarnings("unchecked")
	public void setGroupRanks(boolean b) {
		ActionContext.getContext().getSession()
				.put(SessionKeys.HEATMAP_GROUP_RANKS, Boolean.valueOf(b));
	}

	public void setLabelManagementService(
			LabelManagementService labelManagementService) {
		this.labelManagementService = checkNotNull(labelManagementService);
	}

	public void setSpaceManager(SpaceManager spaceManager) {
		this.spaceManager = checkNotNull(spaceManager);
	}

	/**
	 * Sort a Map by Values
	 * 
	 * @param unsortMap
	 * @return sortedMap
	 */
	private Map<Label, Integer> sortByComparator(Map<Label, Integer> unsortMap) {
		List<Entry<Label, Integer>> list = new LinkedList<Entry<Label, Integer>>(
				unsortMap.entrySet());

		// sort list based on comparator
		Collections.sort(list, new Comparator<Entry<Label, Integer>>() {
			@Override
			public int compare(Entry<Label, Integer> o1,
					Entry<Label, Integer> o2) {
				return o1.getValue() > o2.getValue() ? -1 : 1;
			}
		});

		// put sorted list into map again
		// LinkedHashMap make sure order in which keys were inserted
		Map<Label, Integer> sortedMap = new LinkedHashMap<Label, Integer>();
		for (Iterator<Entry<Label, Integer>> it = list.iterator(); it.hasNext();) {
			Map.Entry<Label, Integer> entry = it.next();
			sortedMap.put(entry.getKey(), entry.getValue());
		}
		return sortedMap;
	}
}
